package org.JWCB.common.file.service;


import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.ObjectId;
import cn.hutool.core.util.StrUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.*;
import lombok.extern.slf4j.Slf4j;
import org.JWCB.common.core.constants.CacheConstants;
import org.JWCB.common.core.constants.UserConstants;
import org.JWCB.common.core.enums.ResultCode;
import org.JWCB.common.core.exception.ServiceException;
import org.JWCB.common.core.utils.ThreadLocalUtil;
import org.JWCB.common.file.config.OSSProperties;
import org.JWCB.common.file.domain.OSSResult;
import org.JWCB.common.redis.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@RefreshScope
public class OSSService {

    @Autowired
    private OSSProperties prop;


    @Autowired
    private OSS ossClient;

    @Autowired
    private RedisService redisService;

    @Value("${file.max-time}")
    private int maxTime;

    @Value("${file.test}")
    private boolean test;


    public OSSResult uploadFile(MultipartFile file,String dirPath) throws Exception {
        if(!test) {
            checkUploadTime();
        }
        InputStream inputStream = null;
        try {
            String fileName;
            if(file.getOriginalFilename() != null) {
                fileName = file.getOriginalFilename().toLowerCase();
            }else {
                fileName = "a.png";
            }
            String extName = fileName.substring(fileName.lastIndexOf(".")+1);
            inputStream = file.getInputStream();
            return upload(extName,inputStream,dirPath);
        }catch (Exception e) {
            log.error("OSS upload file error",e);
            throw new ServiceException(ResultCode.FAILED_FILE_UPLOAD);
        }finally {
            if(inputStream != null) {
                inputStream.close();
            }
        }
    }

    private OSSResult upload(String fileType, InputStream inputStream,String dirPath) throws Exception {
        String key = prop.getPathPrefix() +dirPath +  ObjectId.next() + "." + fileType;
        ObjectMetadata objectMetadata = new ObjectMetadata() ;
        objectMetadata.setObjectAcl(CannedAccessControlList.PublicRead);
        PutObjectRequest request = new PutObjectRequest(prop.getBucketName(),key,inputStream,objectMetadata);
        PutObjectResult putObjectResult;
        try {
            putObjectResult = ossClient.putObject(request);
        }catch (Exception e) {
            log.error("OSS put object error:{}", ExceptionUtil.stacktraceToOneLineString(e,500));
            throw new ServiceException(ResultCode.FAILED_FILE_UPLOAD);
        }
        return assembleOssResult(key,putObjectResult);
    }

    private OSSResult assembleOssResult(String key, PutObjectResult putObjectResult) {
        OSSResult ossResult = new OSSResult();
        if(putObjectResult == null || StrUtil.isBlank(putObjectResult.getRequestId())) {
            ossResult.setSuccess(false);
        }else {
            ossResult.setSuccess(true);
            ossResult.setName(FileUtil.getName(key));
        }
        return ossResult;
    }


    /**
     * 限制用户每天上传次数
     */
    private void checkUploadTime() {
        Long userId = ThreadLocalUtil.get(UserConstants.USER_ID, Long.class);
        Long times = redisService.getCacheMapValue(CacheConstants.USER_UPLOAD_TIMES_KEY,String.valueOf(userId),Long.class);
        if(times != null && times >= maxTime) {
            throw new ServiceException(ResultCode.MAXIMUM_NUM_OF_TIMES_UPLOAD);
        }
        redisService.incrementHashValue(CacheConstants.USER_UPLOAD_TIMES_KEY,String.valueOf(userId),1);
        if(times == null || times == 0) {
            long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(),
                    LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0));
            redisService.expire(CacheConstants.USER_UPLOAD_TIMES_KEY,seconds, TimeUnit.SECONDS);
        }
    }

    /**
     * 转储单个文件
     *
     * @param sourceKey 原路径
     * @param targetKey 新路径
     * @return
     */

    public void transferFile(String sourceKey, String targetKey) {
        try {

            sourceKey = prop.getPathPrefix() + sourceKey;
            targetKey = prop.getPathPrefix() + targetKey;
            // 复制文件
            CopyObjectRequest copyObjectRequest = new CopyObjectRequest(
                    prop.getBucketName(), sourceKey,
                    prop.getBucketName(), targetKey);
            CopyObjectResult copyObjectResult = ossClient.copyObject(copyObjectRequest);

            if (copyObjectResult == null || StrUtil.isBlank(copyObjectResult.getETag())) {
                throw new ServiceException(ResultCode.FAILED_FILE_UPLOAD);
            }

        } catch (Exception e) {
            log.error("文件转储失败:", e);
            throw new ServiceException(ResultCode.FAILED_FILE_UPLOAD);
        }
    }

}
